#if defined(__linux__)
#define _DEFAULT_SOURCE
#include <sys/mman.h>
#endif

#include "fd_aes_base.h"
#include "fd_aes_gcm.h"

#if FD_USING_GCC && __GNUC__ >= 15
#pragma GCC diagnostic ignored "-Wunterminated-string-initialization"
#endif

/* Key expansion tests ************************************************/

static uchar const __attribute__((aligned(16UL)))
fixture_key_expansion_128_zeros[ 176 ] = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63, 0x62, 0x63, 0x63, 0x63,
  0x9b, 0x98, 0x98, 0xc9, 0xf9, 0xfb, 0xfb, 0xaa, 0x9b, 0x98, 0x98, 0xc9, 0xf9, 0xfb, 0xfb, 0xaa,
  0x90, 0x97, 0x34, 0x50, 0x69, 0x6c, 0xcf, 0xfa, 0xf2, 0xf4, 0x57, 0x33, 0x0b, 0x0f, 0xac, 0x99,
  0xee, 0x06, 0xda, 0x7b, 0x87, 0x6a, 0x15, 0x81, 0x75, 0x9e, 0x42, 0xb2, 0x7e, 0x91, 0xee, 0x2b,
  0x7f, 0x2e, 0x2b, 0x88, 0xf8, 0x44, 0x3e, 0x09, 0x8d, 0xda, 0x7c, 0xbb, 0xf3, 0x4b, 0x92, 0x90,
  0xec, 0x61, 0x4b, 0x85, 0x14, 0x25, 0x75, 0x8c, 0x99, 0xff, 0x09, 0x37, 0x6a, 0xb4, 0x9b, 0xa7,
  0x21, 0x75, 0x17, 0x87, 0x35, 0x50, 0x62, 0x0b, 0xac, 0xaf, 0x6b, 0x3c, 0xc6, 0x1b, 0xf0, 0x9b,
  0x0e, 0xf9, 0x03, 0x33, 0x3b, 0xa9, 0x61, 0x38, 0x97, 0x06, 0x0a, 0x04, 0x51, 0x1d, 0xfa, 0x9f,
  0xb1, 0xd4, 0xd8, 0xe2, 0x8a, 0x7d, 0xb9, 0xda, 0x1d, 0x7b, 0xb3, 0xde, 0x4c, 0x66, 0x49, 0x41,
  0xb4, 0xef, 0x5b, 0xcb, 0x3e, 0x92, 0xe2, 0x11, 0x23, 0xe9, 0x51, 0xcf, 0x6f, 0x8f, 0x18, 0x8e
};

void
test_key_expansion_zeros( ulong         bit_cnt,
                          uchar const * expected,
                          int           expected_round_cnt ) {

  ulong expanded_key_sz = 176UL + ( (bit_cnt-128UL)>>1UL );

  static uchar const key[ 32 ] = {0};

  fd_aes_key_t expanded[1];

  fd_memset( expanded, 0, sizeof(fd_aes_key_t) );
  fd_aes_ref_set_encrypt_key( key, bit_cnt, expanded );
  FD_TEST( 0==memcmp( expanded, expected, expanded_key_sz ) );
  FD_TEST( expanded->rounds == expected_round_cnt );
  FD_LOG_INFO(( "OK: AES-%lu encrypt key expansion (ref)", bit_cnt ));

}

/* AES-ECB tests ******************************************************/

/* Source:  "AES Known Answer Test (KAT) Vectors" by NIST
   https://csrc.nist.gov/projects/cryptographic-algorithm-validation-program/block-ciphers#AES */

struct fd_aes_128_ecb_fixture {
  uchar const k[ 16 ];
  uchar const p[ 16 ];
  uchar const c[ 16 ];
};

typedef struct fd_aes_128_ecb_fixture fd_aes_128_ecb_fixture_t;

static fd_aes_128_ecb_fixture_t const
fd_aes_128_ecb_test_vec[] = {
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x3a\xd7\x8e\x72\x6c\x1e\xc0\x2b\x7e\xbf\xe9\x2b\x23\xd9\xec\x34" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xaa\xe5\x93\x9c\x8e\xfd\xf2\xf0\x4e\x60\xb9\xfe\x71\x17\xb2\xc2" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xf0\x31\xd4\xd7\x4f\x5d\xcb\xf3\x9d\xaa\xf8\xca\x3a\xf6\xe5\x27" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x96\xd9\xfd\x5c\xc4\xf0\x74\x41\x72\x7d\xf0\xf3\x3e\x40\x1a\x36" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x30\xcc\xdb\x04\x46\x46\xd7\xe1\xf3\xcc\xea\x3d\xca\x08\xb8\xc0" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x16\xae\x4c\xe5\x04\x2a\x67\xee\x8e\x17\x7b\x7c\x58\x7e\xcc\x82" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xb6\xda\x0b\xb1\x1a\x23\x85\x5d\x9c\x5c\xb1\xb4\xc6\x41\x2e\x0a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xdb\x4f\x1a\xa5\x30\x96\x7d\x67\x32\xce\x47\x15\xeb\x0e\xe2\x4b" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xa8\x17\x38\x25\x26\x21\xdd\x18\x0a\x34\xf3\x45\x5b\x4b\xaa\x2f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x77\xe2\xb5\x08\xdb\x7f\xd8\x92\x34\xca\xf7\x93\x9e\xe5\x62\x1a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xb8\x49\x9c\x25\x1f\x84\x42\xee\x13\xf0\x93\x3b\x68\x8f\xcd\x19" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x96\x51\x35\xf8\xa8\x1f\x25\xc9\xd6\x30\xb1\x75\x02\xf6\x8e\x53" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x8b\x87\x14\x5a\x01\xad\x1c\x6c\xed\xe9\x95\xea\x36\x70\x45\x4f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x8e\xae\x3b\x10\xa0\xc8\xca\x6d\x1d\x3b\x0f\xa6\x1e\x56\xb0\xb2" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x64\xb4\xd6\x29\x81\x0f\xda\x6b\xaf\xdf\x08\xf3\xb0\xd8\xd2\xc5" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xd7\xe5\xdb\xd3\x32\x45\x95\xf8\xfd\xc7\xd7\xc5\x71\xda\x6c\x2a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xf3\xf7\x23\x75\x26\x4e\x16\x7f\xca\x9d\xe2\xc1\x52\x7d\x96\x06" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x8e\xe7\x9d\xd4\xf4\x01\xff\x9b\x7e\xa9\x45\xd8\x66\x66\xc1\x3b" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xdd\x35\xce\xa2\x79\x99\x40\xb4\x0d\xb3\xf8\x19\xcb\x94\xc0\x8b" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x69\x41\xcb\x6b\x3e\x08\xc2\xb7\xaf\xa5\x81\xeb\xdd\x60\x7b\x87" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x2c\x20\xf4\x39\xf6\xbb\x09\x7b\x29\xb8\xbd\x6d\x99\xaa\xd7\x99" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x62\x5d\x01\xf0\x58\xe5\x65\xf7\x7a\xe8\x63\x78\xbd\x2c\x49\xb3" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xc0\xb5\xfd\x98\x19\x0e\xf4\x5f\xbb\x43\x01\x43\x8d\x09\x59\x50" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x13\x00\x1f\xf5\xd9\x98\x06\xef\xd2\x5d\xa3\x4f\x56\xbe\x85\x4b" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x3b\x59\x4c\x60\xf5\xc8\x27\x7a\x51\x13\x67\x7f\x94\x20\x8d\x82" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xe9\xc0\xfc\x18\x18\xe4\xaa\x46\xbd\x2e\x39\xd6\x38\xf8\x9e\x05" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xf8\x02\x3e\xe9\xc3\xfd\xc4\x5a\x01\x9b\x4e\x98\x5c\x7e\x1a\x54" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x35\xf4\x01\x82\xab\x46\x62\xf3\x02\x3b\xae\xc1\xee\x79\x6b\x57" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x3a\xeb\xba\xd7\x30\x36\x49\xb4\x19\x4a\x69\x45\xc6\xcc\x36\x94" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xa2\x12\x4b\xea\x53\xec\x28\x34\x27\x9b\xed\x7f\x7e\xb0\xf9\x38" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xb9\xfb\x43\x99\xfa\x4f\xac\xc7\x30\x9e\x14\xec\x98\x36\x0b\x0a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xc2\x62\x77\x43\x74\x20\xc5\xd6\x34\xf7\x15\xae\xa8\x1a\x91\x32" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x17\x1a\x0e\x1b\x2d\xd4\x24\xf0\xe0\x89\xaf\x2c\x4c\x10\xf3\x2f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x7c\xad\xbe\x40\x2d\x1b\x20\x8f\xe7\x35\xed\xce\x00\xae\xe7\xce" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x43\xb0\x2f\xf9\x29\xa1\x48\x5a\xf6\xf5\xc6\xd6\x55\x8b\xaa\x0f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x09\x2f\xaa\xcc\x9b\xf4\x35\x08\xbf\x8f\xa8\x61\x3c\xa7\x5d\xea" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xcb\x2b\xf8\x28\x0f\x3f\x97\x42\xc7\xed\x51\x3f\xe8\x02\x62\x9c" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x21\x5a\x41\xee\x44\x2f\xa9\x92\xa6\xe3\x23\x98\x6d\xed\x3f\x68" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xf2\x1e\x99\xcf\x4f\x0f\x77\xce\xa8\x36\xe1\x1a\x2f\xe7\x5f\xb1" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x95\xe3\xa0\xca\x90\x79\xe6\x46\x33\x1d\xf8\xb4\xe7\x0d\x2c\xd6" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x4a\xfe\x7f\x12\x0c\xe7\x61\x3f\x74\xfc\x12\xa0\x1a\x82\x80\x73" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x82\x7f\x00\x0e\x75\xe2\xc8\xb9\xd4\x79\xbe\xed\x91\x3f\xe6\x78" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x35\x83\x0c\x8e\x7a\xae\xfe\x2d\x30\x31\x0e\xf3\x81\xcb\xf6\x91" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x19\x1a\xa0\xf2\xc8\x57\x01\x44\xf3\x86\x57\xea\x40\x85\xeb\xe5" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x85\x06\x2c\x2c\x90\x9f\x15\xd9\x26\x9b\x6c\x18\xce\x99\xc4\xf0" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x67\x80\x34\xdc\x9e\x41\xb5\xa5\x60\xed\x23\x9e\xea\xb1\xbc\x78" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xc2\xf9\x3a\x4c\xe5\xab\x6d\x5d\x56\xf1\xb9\x3c\xf1\x99\x11\xc1" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x1c\x31\x12\xbc\xb0\xc1\xdc\xc7\x49\xd7\x99\x74\x36\x91\xbf\x82" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x00\xc5\x5b\xd7\x5c\x7f\x9c\x88\x19\x89\xd3\xec\x19\x11\xc0\xd4" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xea\x2e\x6b\x5e\xf1\x82\xb7\xdf\xf3\x62\x9a\xbd\x6a\x12\x04\x5f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x22\x32\x23\x27\xe0\x17\x80\xb1\x73\x97\xf2\x40\x87\xf8\xcc\x6f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xc9\xca\xcb\x5c\xd1\x16\x92\xc3\x73\xb2\x41\x17\x68\x14\x9e\xe7" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xa1\x8e\x3d\xbb\xca\x57\x78\x60\xda\xb6\xb8\x0d\xa3\x13\x92\x56" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x79\xb6\x1c\x37\xbf\x32\x8e\xcc\xa8\xd7\x43\x26\x5a\x3d\x42\x5c" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xd2\xd9\x9c\x6b\xcc\x1f\x06\xfd\xa8\xe2\x7e\x8a\xe3\xf1\xcc\xc7" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x1b\xfd\x4b\x91\xc7\x01\xfd\x6b\x61\xb7\xf9\x97\x82\x9d\x66\x3b" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x11\x00\x5d\x52\xf2\x5f\x16\xbd\xc9\x54\x5a\x87\x6a\x63\x49\x0a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x3a\x4d\x35\x4f\x02\xbb\x5a\x5e\x47\xd3\x96\x66\x86\x7f\x24\x6a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xd4\x51\xb8\xd6\xe1\xe1\xa0\xeb\xb1\x55\xfb\xbf\x6e\x7b\x7d\xc3" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x68\x98\xd4\xf4\x2f\xa7\xba\x6a\x10\xac\x05\xe8\x7b\x9f\x20\x80" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xb6\x11\x29\x5e\x73\x9c\xa7\xd9\xb5\x0f\x8e\x4c\x0e\x75\x4a\x3f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x7d\x33\xfc\x7d\x8a\xbe\x3c\xa1\x93\x67\x59\xf8\xf5\xde\xaf\x20" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00\x00", .c="\x3b\x5e\x0f\x56\x6d\xc9\x6c\x29\x8f\x0c\x12\x63\x75\x39\xb2\x5c" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00", .c="\xf8\x07\xc3\xe7\x98\x5f\xe0\xf5\xa5\x0e\x2c\xdb\x25\xc5\x10\x9e" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00\x00", .c="\x41\xf9\x92\xa8\x56\xfb\x27\x8b\x38\x9a\x62\xf5\xd2\x74\xd7\xe9" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00\x00", .c="\x10\xd3\xed\x7a\x6f\xe1\x5a\xb4\xd9\x1a\xcb\xc7\xd0\x76\x7a\xb1" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00\x00", .c="\x21\xfe\xec\xd4\x5b\x2e\x67\x59\x73\xac\x33\xbf\x0c\x54\x24\xfc" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00\x00", .c="\x14\x80\xcb\x39\x55\xba\x62\xd0\x9e\xea\x66\x8f\x7c\x70\x88\x17" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00\x00", .c="\x66\x40\x40\x33\xd6\xb7\x2b\x60\x93\x54\xd5\x49\x6e\x7e\xb5\x11" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00\x00", .c="\x1c\x31\x7a\x22\x0a\x7d\x70\x0d\xa2\xb1\xe0\x75\xb0\x02\x66\xe1" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00\x00", .c="\xab\x3b\x89\x54\x22\x33\xf1\x27\x1b\xf8\xfd\x0c\x0f\x40\x35\x45" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00", .c="\xd9\x3e\xae\x96\x6f\xac\x46\xdc\xa9\x27\xd6\xb1\x14\xfa\x3f\x9e" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00\x00", .c="\x1b\xde\xc5\x21\x31\x65\x03\xd9\xd5\xee\x65\xdf\x3e\xa9\x4d\xdf" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00\x00", .c="\xee\xf4\x56\x43\x1d\xea\x8b\x4a\xcf\x83\xbd\xae\x37\x17\xf7\x5f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00\x00", .c="\x06\xf2\x51\x9a\x2f\xaf\xaa\x59\x6b\xfe\xf5\xcf\xa1\x5c\x21\xb9" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00\x00", .c="\x25\x1a\x7e\xac\x7e\x2f\xe8\x09\xe4\xaa\x8d\x0d\x70\x12\x53\x1a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00\x00", .c="\x3b\xff\xc1\x6e\x4c\x49\xb2\x68\xa2\x0f\x8d\x96\xa6\x0b\x40\x58" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00\x00", .c="\xe8\x86\xf9\x28\x19\x99\xc5\xbb\x3b\x3e\x88\x62\xe2\xf7\xc9\x88" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00\x00", .c="\x56\x3b\xf9\x0d\x61\xbe\xef\x39\xf4\x8d\xd6\x25\xfc\xef\x13\x61" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00", .c="\x4d\x37\xc8\x50\x64\x45\x63\xc6\x9f\xd0\xac\xd9\xa0\x49\x32\x5b" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00\x00", .c="\xb8\x7c\x92\x1b\x91\x82\x9e\xf3\xb1\x3c\xa5\x41\xee\x11\x30\xa6" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00\x00", .c="\x2e\x65\xeb\x6b\x6e\xa3\x83\xe1\x09\xac\xcc\xe8\x32\x6b\x03\x93" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00\x00", .c="\x9c\xa5\x47\xf7\x43\x9e\xdc\x3e\x25\x5c\x0f\x4d\x49\xaa\x89\x90" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00\x00", .c="\xa5\xe6\x52\x61\x4c\x93\x00\xf3\x78\x16\xb1\xf9\xfd\x0c\x87\xf9" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00\x00", .c="\x14\x95\x4f\x0b\x46\x97\x77\x6f\x44\x49\x4f\xe4\x58\xd8\x14\xed" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00\x00", .c="\x7c\x8d\x9a\xb6\xc2\x76\x17\x23\xfe\x42\xf8\xbb\x50\x6c\xbc\xf7" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00\x00", .c="\xdb\x7e\x19\x32\x67\x9f\xdd\x99\x74\x2a\xab\x04\xaa\x0d\x5a\x80" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00", .c="\x4c\x6a\x1c\x83\xe5\x68\xcd\x10\xf2\x7c\x2d\x73\xde\xd1\x9c\x28" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00\x00", .c="\x90\xec\xbe\x61\x77\xe6\x74\xc9\x8d\xe4\x12\x41\x3f\x7a\xc9\x15" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00\x00", .c="\x90\x68\x4a\x2a\xc5\x5f\xe1\xec\x2b\x8e\xbd\x56\x22\x52\x0b\x73" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00\x00", .c="\x74\x72\xf9\xa7\x98\x86\x07\xca\x79\x70\x77\x95\x99\x10\x35\xe6" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00\x00", .c="\x56\xaf\xf0\x89\x87\x8b\xf3\x35\x2f\x8d\xf1\x72\xa3\xae\x47\xd8" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00\x00", .c="\x65\xc0\x52\x6c\xbe\x40\x16\x1b\x80\x19\xa2\xa3\x17\x1a\xbd\x23" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00\x00", .c="\x37\x7b\xe0\xbe\x33\xb4\xe3\xe3\x10\xb4\xaa\xbd\xa1\x73\xf8\x4f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00\x00", .c="\x94\x02\xe9\xaa\x6f\x69\xde\x65\x04\xda\x8d\x20\xc4\xfc\xaa\x2f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00", .c="\x12\x3c\x1f\x4a\xf3\x13\xad\x8c\x2c\xe6\x48\xb2\xe7\x1f\xb6\xe1" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00\x00", .c="\x1f\xfc\x62\x6d\x30\x20\x3d\xcd\xb0\x01\x9f\xb8\x0f\x72\x6c\xf4" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00\x00", .c="\x76\xda\x1f\xbe\x3a\x50\x72\x8c\x50\xfd\x2e\x62\x1b\x5a\xd8\x85" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00\x00", .c="\x08\x2e\xb8\xbe\x35\xf4\x42\xfb\x52\x66\x8e\x16\xa5\x91\xd1\xd6" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00\x00", .c="\xe6\x56\xf9\xec\xf5\xfe\x27\xec\x3e\x4a\x73\xd0\x0c\x28\x2f\xb3" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00\x00", .c="\x2c\xa8\x20\x9d\x63\x27\x4c\xd9\xa2\x9b\xb7\x4b\xcd\x77\x68\x3a" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00\x00", .c="\x79\xbf\x5d\xce\x14\xbb\x7d\xd7\x3a\x8e\x36\x11\xde\x7c\xe0\x26" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00\x00", .c="\x3c\x84\x99\x39\xa5\xd2\x93\x99\xf3\x44\xc4\xa0\xec\xa8\xa5\x76" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00", .c="\xed\x3c\x0a\x94\xd5\x9b\xec\xe9\x88\x35\xda\x7a\xa4\xf0\x7c\xa2" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00\x00", .c="\x63\x91\x9e\xd4\xce\x10\x19\x64\x38\xb6\xad\x09\xd9\x9c\xd7\x95" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00\x00", .c="\x76\x78\xf3\xa8\x33\xf1\x9f\xea\x95\xf3\xc6\x02\x9e\x2b\xc6\x10" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00\x00", .c="\x3a\xa4\x26\x83\x10\x67\xd3\x6b\x92\xbe\x7c\x5f\x81\xc1\x3c\x56" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00\x00", .c="\x92\x72\xe2\xd2\xcd\xd1\x10\x50\x99\x8c\x84\x50\x77\xa3\x0e\xa0" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00\x00", .c="\x08\x8c\x4b\x53\xf5\xec\x0f\xf8\x14\xc1\x9a\xda\xe7\xf6\x24\x6c" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00\x00", .c="\x40\x10\xa5\xe4\x01\xfd\xf0\xa0\x35\x4d\xdb\xcc\x0d\x01\x2b\x17" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00\x00", .c="\xa8\x7a\x38\x57\x36\xc0\xa6\x18\x9b\xd6\x58\x9b\xd8\x44\x5a\x93" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00", .c="\x54\x5f\x2b\x83\xd9\x61\x6d\xcc\xf6\x0f\xa9\x83\x0e\x9c\xd2\x87" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80\x00", .c="\x4b\x70\x6f\x7f\x92\x40\x63\x52\x39\x40\x37\xa6\xd4\xf4\x68\x8d" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0\x00", .c="\xb7\x97\x2b\x39\x41\xc4\x4b\x90\xaf\xa7\xb2\x64\xbf\xba\x73\x87" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0\x00", .c="\x6f\x45\x73\x2c\xf1\x08\x81\x54\x6f\x0f\xd2\x38\x96\xd2\xbb\x60" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0\x00", .c="\x2e\x35\x79\xca\x15\xaf\x27\xf6\x4b\x3c\x95\x5a\x5b\xfc\x30\xba" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8\x00", .c="\x34\xa2\xc5\xa9\x1a\xe2\xae\xc9\x9b\x7d\x1b\x5f\xa6\x78\x04\x47" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc\x00", .c="\xa4\xd6\x61\x6b\xd0\x4f\x87\x33\x5b\x0e\x53\x35\x12\x27\xa9\xee" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe\x00", .c="\x7f\x69\x2b\x03\x94\x58\x67\xd1\x61\x79\xa8\xce\xfc\x83\xea\x3f" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x00", .c="\x3b\xd1\x41\xee\x84\xa0\xe6\x41\x4a\x26\xe7\xa4\xf2\x81\xf8\xa2" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x80", .c="\xd1\x78\x8f\x57\x2d\x98\xb2\xb1\x6e\xc5\xd5\xf3\x92\x2b\x99\xbc" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xc0", .c="\x08\x33\xff\x6f\x61\xd9\x8a\x57\xb2\x88\xe8\xc3\x58\x6b\x85\xa6" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xe0", .c="\x85\x68\x26\x17\x97\xde\x17\x6b\xf0\xb4\x3b\xec\xc6\x28\x5a\xfb" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf0", .c="\xf9\xb0\xfd\xa0\xc4\xa8\x98\xf5\xb9\xe6\xf6\x61\xc4\xce\x4d\x07" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xf8", .c="\x8a\xde\x89\x59\x13\x68\x5c\x67\xc5\x26\x9f\x8a\xae\x42\x98\x3e" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfc", .c="\x39\xbd\xe6\x7d\x5c\x8e\xd8\xa8\xb1\xc3\x7e\xb8\xfa\x9f\x5a\xc0" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xfe", .c="\x5c\x00\x5e\x72\xc1\x41\x8c\x44\xf5\x69\xf2\xea\x33\xba\x54\xf3" },
  { .k="\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", .p="\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", .c="\x3f\x5b\x8c\xc9\xea\x85\x5a\x0a\xfa\x73\x47\xd2\x3e\x8d\x66\x4e" }
};

void
test_aes_128_ecb_( fd_aes_128_ecb_fixture_t const * ecb ) {

  fd_aes_key_t key[1];
  uchar c[ 16 ];
  uchar p[ 16 ];

  fd_aes_ref_set_encrypt_key( ecb->k, 128, key );
  fd_aes_ref_encrypt_core( ecb->p, c, key );
  FD_TEST( 0==memcmp( c, ecb->c, 16 ) );

  fd_aes_ref_set_decrypt_key( ecb->k, 128, key );
  fd_aes_ref_decrypt_core( ecb->c, p, key );
  FD_TEST( 0==memcmp( p, ecb->p, 16 ) );

}

void
test_aes_128_ecb_api( fd_aes_128_ecb_fixture_t const * ecb ) {

  fd_aes_key_t key[1];
  uchar c[ 16 ];
  uchar p[ 16 ];

  fd_aes_set_encrypt_key( ecb->k, 128, key );
  fd_aes_encrypt( ecb->p, c, key );
  FD_TEST( 0==memcmp( c, ecb->c, 16 ) );

  fd_aes_set_decrypt_key( ecb->k, 128, key );
  fd_aes_decrypt( ecb->c, p, key );
  FD_TEST( 0==memcmp( p, ecb->p, 16 ) );

}

void
test_aes_128_ecb( void ) {
  fd_aes_128_ecb_fixture_t const * v  = fd_aes_128_ecb_test_vec;
  fd_aes_128_ecb_fixture_t const * v1 = v + sizeof(fd_aes_128_ecb_test_vec)/sizeof(fd_aes_128_ecb_fixture_t);

  while( v<v1 ) {
    test_aes_128_ecb_( v++ );
    test_aes_128_ecb_api( v++ );
  }

  FD_LOG_INFO(( "OK: AES-128-ECB encrypt+decrypt (ref)" ));
}

/* AEAD Decrypt *******************************************************/

void
test_aes_128_gcm_bounds( fd_rng_t * rng ) {
# if defined(__linux__)

  /* Execute AES-GCM ops just before unmapped memory to trap
     out-of-bounds accesses.  ASan is uneffective here because memory
     accesses occur from uninstrumented assembly blobs. */

  ulong region_sz = (1UL<<30);
  uchar * ptr_p     = mmap( NULL, region_sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
  uchar * ptr_c     = mmap( NULL, region_sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
  uchar * state_mem = mmap( NULL, region_sz, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0 );
  FD_TEST( ptr_p    !=MAP_FAILED );
  FD_TEST( ptr_c    !=MAP_FAILED );
  FD_TEST( state_mem!=MAP_FAILED );

  uchar * ptr_p_end = ptr_p     + FD_SHMEM_NORMAL_PAGE_SZ;
  uchar * ptr_c_end = ptr_c     + FD_SHMEM_NORMAL_PAGE_SZ;
  uchar * state_end = state_mem + FD_SHMEM_NORMAL_PAGE_SZ;
  FD_TEST( 0==munmap( ptr_p_end, region_sz - FD_SHMEM_NORMAL_PAGE_SZ ) );
  FD_TEST( 0==munmap( ptr_c_end, region_sz - FD_SHMEM_NORMAL_PAGE_SZ ) );
  FD_TEST( 0==munmap( state_end, region_sz - FD_SHMEM_NORMAL_PAGE_SZ ) );

  fd_memset( ptr_p,     0, FD_SHMEM_NORMAL_PAGE_SZ );
  fd_memset( ptr_c,     0, FD_SHMEM_NORMAL_PAGE_SZ );
  fd_memset( state_mem, 0, FD_SHMEM_NORMAL_PAGE_SZ );

  fd_aes_gcm_t * aes_gcm = fd_type_pun( state_end - sizeof(fd_aes_gcm_t) );
  FD_TEST( fd_ulong_is_aligned( (ulong)aes_gcm, FD_AES_GCM_ALIGN ) );

  uchar const key[ FD_AES_128_KEY_SZ ] = {0};
  uchar const iv [ FD_AES_GCM_IV_SZ  ] = {0};

  for( ulong sz=0UL; sz<=FD_SHMEM_NORMAL_PAGE_SZ; sz++ ) {
    uchar * p = ptr_p_end - sz;
    uchar * c = ptr_c_end - sz;
    uchar tag[16];

    /* Write garbage into the memory region backing the state object to
       detect uninitialized memory reads */
    for( ulong laddr=(ulong)aes_gcm; laddr<(ulong)state_end; laddr+=sizeof(ulong) ) {
      FD_STORE( ulong, laddr, fd_rng_ulong( rng ) );
    }
    fd_aes_128_gcm_init( aes_gcm, key, iv );
    fd_aes_gcm_encrypt( aes_gcm, c, p, sz, p, sz, tag );

    for( ulong laddr=(ulong)aes_gcm; laddr<(ulong)state_end; laddr+=sizeof(ulong) ) {
      FD_STORE( ulong, laddr, fd_rng_ulong( rng ) );
    }
    fd_aes_128_gcm_init( aes_gcm, key, iv );
    int rc = fd_aes_gcm_decrypt( aes_gcm, c, p, sz, p, sz, tag );
    FD_TEST( rc==FD_AES_GCM_DECRYPT_OK );
  }
  for( uchar const * p=ptr_p; p<ptr_p_end; p++ ) FD_TEST( *p==0 );

  FD_TEST( 0==munmap( ptr_p,     FD_SHMEM_NORMAL_PAGE_SZ ) );
  FD_TEST( 0==munmap( ptr_c,     FD_SHMEM_NORMAL_PAGE_SZ ) );
  FD_TEST( 0==munmap( state_mem, FD_SHMEM_NORMAL_PAGE_SZ ) );

# endif /* defined(__linux__) */
}

void
test_aes_128_gcm( void ) {

  uchar const iv[ 12 ] =
    { 0xfa, 0x04, 0x4b, 0x2f, 0x42, 0xa3,
      0xfd, 0x3b, 0x46, 0xfb, 0x25, 0x5e };

  uchar const key[ 16 ] =
    { 0x1f, 0x36, 0x96, 0x13, 0xdd, 0x76, 0xd5, 0x46,
      0x77, 0x30, 0xef, 0xcb, 0xe3, 0xb1, 0xa2, 0x2d };

  uchar const aad[ 22 ] =
    { 0xc3, 0x00, 0x00, 0x00, 0x01, 0x08, 0x83, 0x94,
      0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x00, 0x00,
      0x44, 0x9e, 0x00, 0x00, 0x00, 0x02 };

  uchar const plaintext[ 1162 ] =
    { 0x06, 0x00, 0x40, 0xf1, 0x01, 0x00, 0x00, 0xed, 0x03, 0x03, 0xeb, 0xf8, 0xfa, 0x56, 0xf1, 0x29,
      0x39, 0xb9, 0x58, 0x4a, 0x38, 0x96, 0x47, 0x2e, 0xc4, 0x0b, 0xb8, 0x63, 0xcf, 0xd3, 0xe8, 0x68,
      0x04, 0xfe, 0x3a, 0x47, 0xf0, 0x6a, 0x2b, 0x69, 0x48, 0x4c, 0x00, 0x00, 0x04, 0x13, 0x01, 0x13,
      0x02, 0x01, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, 0x0b, 0x65, 0x78,
      0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a,
      0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x10, 0x00, 0x07, 0x00, 0x05,
      0x04, 0x61, 0x6c, 0x70, 0x6e, 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33,
      0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x93, 0x70, 0xb2, 0xc9, 0xca, 0xa4, 0x7f, 0xba,
      0xba, 0xf4, 0x55, 0x9f, 0xed, 0xba, 0x75, 0x3d, 0xe1, 0x71, 0xfa, 0x71, 0xf5, 0x0f, 0x1c, 0xe1,
      0x5d, 0x43, 0xe9, 0x94, 0xec, 0x74, 0xd7, 0x48, 0x00, 0x2b, 0x00, 0x03, 0x02, 0x03, 0x04, 0x00,
      0x0d, 0x00, 0x10, 0x00, 0x0e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x02, 0x03, 0x08, 0x04, 0x08,
      0x05, 0x08, 0x06, 0x00, 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x1c, 0x00, 0x02, 0x40, 0x01, 0x00,
      0x39, 0x00, 0x32, 0x04, 0x08, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x05, 0x04, 0x80,
      0x00, 0xff, 0xff, 0x07, 0x04, 0x80, 0x00, 0xff, 0xff, 0x08, 0x01, 0x10, 0x01, 0x04, 0x80, 0x00,
      0x75, 0x30, 0x09, 0x01, 0x10, 0x0f, 0x08, 0x83, 0x94, 0xc8, 0xf0, 0x3e, 0x51, 0x57, 0x08, 0x06,
      0x04, 0x80, 0x00, 0xff, 0xff  /* zero padded .... */ };

  uchar const tag[ 16 ] =
    { 0xe2, 0x21, 0xaf, 0x44, 0x86, 0x00, 0x18, 0xab,
      0x08, 0x56, 0x97, 0x2e, 0x19, 0x4c, 0xd9, 0x34 };

  uchar const ciphertext[ 1162 ] =
    { 0xd1, 0xb1, 0xc9, 0x8d, 0xd7, 0x68, 0x9f, 0xb8, 0xec, 0x11, 0xd2, 0x42, 0xb1, 0x23, 0xdc, 0x9b,
      0xd8, 0xba, 0xb9, 0x36, 0xb4, 0x7d, 0x92, 0xec, 0x35, 0x6c, 0x0b, 0xab, 0x7d, 0xf5, 0x97, 0x6d,
      0x27, 0xcd, 0x44, 0x9f, 0x63, 0x30, 0x00, 0x99, 0xf3, 0x99, 0x1c, 0x26, 0x0e, 0xc4, 0xc6, 0x0d,
      0x17, 0xb3, 0x1f, 0x84, 0x29, 0x15, 0x7b, 0xb3, 0x5a, 0x12, 0x82, 0xa6, 0x43, 0xa8, 0xd2, 0x26,
      0x2c, 0xad, 0x67, 0x50, 0x0c, 0xad, 0xb8, 0xe7, 0x37, 0x8c, 0x8e, 0xb7, 0x53, 0x9e, 0xc4, 0xd4,
      0x90, 0x5f, 0xed, 0x1b, 0xee, 0x1f, 0xc8, 0xaa, 0xfb, 0xa1, 0x7c, 0x75, 0x0e, 0x2c, 0x7a, 0xce,
      0x01, 0xe6, 0x00, 0x5f, 0x80, 0xfc, 0xb7, 0xdf, 0x62, 0x12, 0x30, 0xc8, 0x37, 0x11, 0xb3, 0x93,
      0x43, 0xfa, 0x02, 0x8c, 0xea, 0x7f, 0x7f, 0xb5, 0xff, 0x89, 0xea, 0xc2, 0x30, 0x82, 0x49, 0xa0,
      0x22, 0x52, 0x15, 0x5e, 0x23, 0x47, 0xb6, 0x3d, 0x58, 0xc5, 0x45, 0x7a, 0xfd, 0x84, 0xd0, 0x5d,
      0xff, 0xfd, 0xb2, 0x03, 0x92, 0x84, 0x4a, 0xe8, 0x12, 0x15, 0x46, 0x82, 0xe9, 0xcf, 0x01, 0x2f,
      0x90, 0x21, 0xa6, 0xf0, 0xbe, 0x17, 0xdd, 0xd0, 0xc2, 0x08, 0x4d, 0xce, 0x25, 0xff, 0x9b, 0x06,
      0xcd, 0xe5, 0x35, 0xd0, 0xf9, 0x20, 0xa2, 0xdb, 0x1b, 0xf3, 0x62, 0xc2, 0x3e, 0x59, 0x6d, 0x11,
      0xa4, 0xf5, 0xa6, 0xcf, 0x39, 0x48, 0x83, 0x8a, 0x3a, 0xec, 0x4e, 0x15, 0xda, 0xf8, 0x50, 0x0a,
      0x6e, 0xf6, 0x9e, 0xc4, 0xe3, 0xfe, 0xb6, 0xb1, 0xd9, 0x8e, 0x61, 0x0a, 0xc8, 0xb7, 0xec, 0x3f,
      0xaf, 0x6a, 0xd7, 0x60, 0xb7, 0xba, 0xd1, 0xdb, 0x4b, 0xa3, 0x48, 0x5e, 0x8a, 0x94, 0xdc, 0x25,
      0x0a, 0xe3, 0xfd, 0xb4, 0x1e, 0xd1, 0x5f, 0xb6, 0xa8, 0xe5, 0xeb, 0xa0, 0xfc, 0x3d, 0xd6, 0x0b,
      0xc8, 0xe3, 0x0c, 0x5c, 0x42, 0x87, 0xe5, 0x38, 0x05, 0xdb, 0x05, 0x9a, 0xe0, 0x64, 0x8d, 0xb2,
      0xf6, 0x42, 0x64, 0xed, 0x5e, 0x39, 0xbe, 0x2e, 0x20, 0xd8, 0x2d, 0xf5, 0x66, 0xda, 0x8d, 0xd5,
      0x99, 0x8c, 0xca, 0xbd, 0xae, 0x05, 0x30, 0x60, 0xae, 0x6c, 0x7b, 0x43, 0x78, 0xe8, 0x46, 0xd2,
      0x9f, 0x37, 0xed, 0x7b, 0x4e, 0xa9, 0xec, 0x5d, 0x82, 0xe7, 0x96, 0x1b, 0x7f, 0x25, 0xa9, 0x32,
      0x38, 0x51, 0xf6, 0x81, 0xd5, 0x82, 0x36, 0x3a, 0xa5, 0xf8, 0x99, 0x37, 0xf5, 0xa6, 0x72, 0x58,
      0xbf, 0x63, 0xad, 0x6f, 0x1a, 0x0b, 0x1d, 0x96, 0xdb, 0xd4, 0xfa, 0xdd, 0xfc, 0xef, 0xc5, 0x26,
      0x6b, 0xa6, 0x61, 0x17, 0x22, 0x39, 0x5c, 0x90, 0x65, 0x56, 0xbe, 0x52, 0xaf, 0xe3, 0xf5, 0x65,
      0x63, 0x6a, 0xd1, 0xb1, 0x7d, 0x50, 0x8b, 0x73, 0xd8, 0x74, 0x3e, 0xeb, 0x52, 0x4b, 0xe2, 0x2b,
      0x3d, 0xcb, 0xc2, 0xc7, 0x46, 0x8d, 0x54, 0x11, 0x9c, 0x74, 0x68, 0x44, 0x9a, 0x13, 0xd8, 0xe3,
      0xb9, 0x58, 0x11, 0xa1, 0x98, 0xf3, 0x49, 0x1d, 0xe3, 0xe7, 0xfe, 0x94, 0x2b, 0x33, 0x04, 0x07,
      0xab, 0xf8, 0x2a, 0x4e, 0xd7, 0xc1, 0xb3, 0x11, 0x66, 0x3a, 0xc6, 0x98, 0x90, 0xf4, 0x15, 0x70,
      0x15, 0x85, 0x3d, 0x91, 0xe9, 0x23, 0x03, 0x7c, 0x22, 0x7a, 0x33, 0xcd, 0xd5, 0xec, 0x28, 0x1c,
      0xa3, 0xf7, 0x9c, 0x44, 0x54, 0x6b, 0x9d, 0x90, 0xca, 0x00, 0xf0, 0x64, 0xc9, 0x9e, 0x3d, 0xd9,
      0x79, 0x11, 0xd3, 0x9f, 0xe9, 0xc5, 0xd0, 0xb2, 0x3a, 0x22, 0x9a, 0x23, 0x4c, 0xb3, 0x61, 0x86,
      0xc4, 0x81, 0x9e, 0x8b, 0x9c, 0x59, 0x27, 0x72, 0x66, 0x32, 0x29, 0x1d, 0x6a, 0x41, 0x82, 0x11,
      0xcc, 0x29, 0x62, 0xe2, 0x0f, 0xe4, 0x7f, 0xeb, 0x3e, 0xdf, 0x33, 0x0f, 0x2c, 0x60, 0x3a, 0x9d,
      0x48, 0xc0, 0xfc, 0xb5, 0x69, 0x9d, 0xbf, 0xe5, 0x89, 0x64, 0x25, 0xc5, 0xba, 0xc4, 0xae, 0xe8,
      0x2e, 0x57, 0xa8, 0x5a, 0xaf, 0x4e, 0x25, 0x13, 0xe4, 0xf0, 0x57, 0x96, 0xb0, 0x7b, 0xa2, 0xee,
      0x47, 0xd8, 0x05, 0x06, 0xf8, 0xd2, 0xc2, 0x5e, 0x50, 0xfd, 0x14, 0xde, 0x71, 0xe6, 0xc4, 0x18,
      0x55, 0x93, 0x02, 0xf9, 0x39, 0xb0, 0xe1, 0xab, 0xd5, 0x76, 0xf2, 0x79, 0xc4, 0xb2, 0xe0, 0xfe,
      0xb8, 0x5c, 0x1f, 0x28, 0xff, 0x18, 0xf5, 0x88, 0x91, 0xff, 0xef, 0x13, 0x2e, 0xef, 0x2f, 0xa0,
      0x93, 0x46, 0xae, 0xe3, 0x3c, 0x28, 0xeb, 0x13, 0x0f, 0xf2, 0x8f, 0x5b, 0x76, 0x69, 0x53, 0x33,
      0x41, 0x13, 0x21, 0x19, 0x96, 0xd2, 0x00, 0x11, 0xa1, 0x98, 0xe3, 0xfc, 0x43, 0x3f, 0x9f, 0x25,
      0x41, 0x01, 0x0a, 0xe1, 0x7c, 0x1b, 0xf2, 0x02, 0x58, 0x0f, 0x60, 0x47, 0x47, 0x2f, 0xb3, 0x68,
      0x57, 0xfe, 0x84, 0x3b, 0x19, 0xf5, 0x98, 0x40, 0x09, 0xdd, 0xc3, 0x24, 0x04, 0x4e, 0x84, 0x7a,
      0x4f, 0x4a, 0x0a, 0xb3, 0x4f, 0x71, 0x95, 0x95, 0xde, 0x37, 0x25, 0x2d, 0x62, 0x35, 0x36, 0x5e,
      0x9b, 0x84, 0x39, 0x2b, 0x06, 0x10, 0x85, 0x34, 0x9d, 0x73, 0x20, 0x3a, 0x4a, 0x13, 0xe9, 0x6f,
      0x54, 0x32, 0xec, 0x0f, 0xd4, 0xa1, 0xee, 0x65, 0xac, 0xcd, 0xd5, 0xe3, 0x90, 0x4d, 0xf5, 0x4c,
      0x1d, 0xa5, 0x10, 0xb0, 0xff, 0x20, 0xdc, 0xc0, 0xc7, 0x7f, 0xcb, 0x2c, 0x0e, 0x0e, 0xb6, 0x05,
      0xcb, 0x05, 0x04, 0xdb, 0x87, 0x63, 0x2c, 0xf3, 0xd8, 0xb4, 0xda, 0xe6, 0xe7, 0x05, 0x76, 0x9d,
      0x1d, 0xe3, 0x54, 0x27, 0x01, 0x23, 0xcb, 0x11, 0x45, 0x0e, 0xfc, 0x60, 0xac, 0x47, 0x68, 0x3d,
      0x7b, 0x8d, 0x0f, 0x81, 0x13, 0x65, 0x56, 0x5f, 0xd9, 0x8c, 0x4c, 0x8e, 0xb9, 0x36, 0xbc, 0xab,
      0x8d, 0x06, 0x9f, 0xc3, 0x3b, 0xd8, 0x01, 0xb0, 0x3a, 0xde, 0xa2, 0xe1, 0xfb, 0xc5, 0xaa, 0x46,
      0x3d, 0x08, 0xca, 0x19, 0x89, 0x6d, 0x2b, 0xf5, 0x9a, 0x07, 0x1b, 0x85, 0x1e, 0x6c, 0x23, 0x90,
      0x52, 0x17, 0x2f, 0x29, 0x6b, 0xfb, 0x5e, 0x72, 0x40, 0x47, 0x90, 0xa2, 0x18, 0x10, 0x14, 0xf3,
      0xb9, 0x4a, 0x4e, 0x97, 0xd1, 0x17, 0xb4, 0x38, 0x13, 0x03, 0x68, 0xcc, 0x39, 0xdb, 0xb2, 0xd1,
      0x98, 0x06, 0x5a, 0xe3, 0x98, 0x65, 0x47, 0x92, 0x6c, 0xd2, 0x16, 0x2f, 0x40, 0xa2, 0x9f, 0x0c,
      0x3c, 0x87, 0x45, 0xc0, 0xf5, 0x0f, 0xba, 0x38, 0x52, 0xe5, 0x66, 0xd4, 0x45, 0x75, 0xc2, 0x9d,
      0x39, 0xa0, 0x3f, 0x0c, 0xda, 0x72, 0x19, 0x84, 0xb6, 0xf4, 0x40, 0x59, 0x1f, 0x35, 0x5e, 0x12,
      0xd4, 0x39, 0xff, 0x15, 0x0a, 0xab, 0x76, 0x13, 0x49, 0x9d, 0xbd, 0x49, 0xad, 0xab, 0xc8, 0x67,
      0x6e, 0xef, 0x02, 0x3b, 0x15, 0xb6, 0x5b, 0xfc, 0x5c, 0xa0, 0x69, 0x48, 0x10, 0x9f, 0x23, 0xf3,
      0x50, 0xdb, 0x82, 0x12, 0x35, 0x35, 0xeb, 0x8a, 0x74, 0x33, 0xbd, 0xab, 0xcb, 0x90, 0x92, 0x71,
      0xa6, 0xec, 0xbc, 0xb5, 0x8b, 0x93, 0x6a, 0x88, 0xcd, 0x4e, 0x8f, 0x2e, 0x6f, 0xf5, 0x80, 0x01,
      0x75, 0xf1, 0x13, 0x25, 0x3d, 0x8f, 0xa9, 0xca, 0x88, 0x85, 0xc2, 0xf5, 0x52, 0xe6, 0x57, 0xdc,
      0x60, 0x3f, 0x25, 0x2e, 0x1a, 0x8e, 0x30, 0x8f, 0x76, 0xf0, 0xbe, 0x79, 0xe2, 0xfb, 0x8f, 0x5d,
      0x5f, 0xbb, 0xe2, 0xe3, 0x0e, 0xca, 0xdd, 0x22, 0x07, 0x23, 0xc8, 0xc0, 0xae, 0xa8, 0x07, 0x8c,
      0xdf, 0xcb, 0x38, 0x68, 0x26, 0x3f, 0xf8, 0xf0, 0x94, 0x00, 0x54, 0xda, 0x48, 0x78, 0x18, 0x93,
      0xa7, 0xe4, 0x9a, 0xd5, 0xaf, 0xf4, 0xaf, 0x30, 0x0c, 0xd8, 0x04, 0xa6, 0xb6, 0x27, 0x9a, 0xb3,
      0xff, 0x3a, 0xfb, 0x64, 0x49, 0x1c, 0x85, 0x19, 0x4a, 0xab, 0x76, 0x0d, 0x58, 0xa6, 0x06, 0x65,
      0x4f, 0x9f, 0x44, 0x00, 0xe8, 0xb3, 0x85, 0x91, 0x35, 0x6f, 0xbf, 0x64, 0x25, 0xac, 0xa2, 0x6d,
      0xc8, 0x52, 0x44, 0x25, 0x9f, 0xf2, 0xb1, 0x9c, 0x41, 0xb9, 0xf9, 0x6f, 0x3c, 0xa9, 0xec, 0x1d,
      0xde, 0x43, 0x4d, 0xa7, 0xd2, 0xd3, 0x92, 0xb9, 0x05, 0xdd, 0xf3, 0xd1, 0xf9, 0xaf, 0x93, 0xd1,
      0xaf, 0x59, 0x50, 0xbd, 0x49, 0x3f, 0x5a, 0xa7, 0x31, 0xb4, 0x05, 0x6d, 0xf3, 0x1b, 0xd2, 0x67,
      0xb6, 0xb9, 0x0a, 0x07, 0x98, 0x31, 0xaa, 0xf5, 0x79, 0xbe, 0x0a, 0x39, 0x01, 0x31, 0x37, 0xaa,
      0xc6, 0xd4, 0x04, 0xf5, 0x18, 0xcf, 0xd4, 0x68, 0x40, 0x64, 0x7e, 0x78, 0xbf, 0xe7, 0x06, 0xca,
      0x4c, 0xf5, 0xe9, 0xc5, 0x45, 0x3e, 0x9f, 0x7c, 0xfd, 0x2b, 0x8b, 0x4c, 0x8d, 0x16, 0x9a, 0x44,
      0xe5, 0x5c, 0x88, 0xd4, 0xa9, 0xa7, 0xf9, 0x47, 0x42, 0x41 };

  /* Test Encrypt */

  static uchar actual_ciphertext[ 1162 ];
  static uchar actual_tag       [   16 ];

  fd_aes_gcm_t gcm[1];
  fd_aes_128_gcm_init( gcm, key, iv );
  fd_aes_gcm_encrypt( gcm, actual_ciphertext, plaintext, sizeof(plaintext), aad, sizeof(aad), actual_tag );

  FD_TEST( 0==memcmp( actual_ciphertext, ciphertext, sizeof( ciphertext ) ) );
  FD_TEST( 0==memcmp( actual_tag,        tag,        sizeof( tag        ) ) );

  FD_LOG_INFO(( "OK: AES-128-GCM encrypt (AES-NI)" ));

  /* Test Decrypt */

  static uchar actual_plaintext[ 1162 ];

  fd_aes_128_gcm_init( gcm, key, iv );
  int decrypt_ok = fd_aes_gcm_decrypt( gcm, actual_ciphertext, actual_plaintext, sizeof(ciphertext), aad, sizeof(aad), tag );

  FD_TEST( decrypt_ok );
  FD_TEST( 0==memcmp( actual_plaintext, plaintext, sizeof( plaintext ) ) );

  FD_LOG_INFO(( "OK: AES-128-GCM decrypt (AES-NI)" ));

  /* Test AEAD malleability */

# define BITFLIP( x, i ) ( (x)[ (i)>>3 ] = (uchar)( (x)[ (i)>>3 ] ^ 1UL<<((i)&7UL) ) )

  /* Corrupt tag */
  for( ulong i=0UL; i<128UL; i++ ) {
    BITFLIP( actual_tag, i );
    fd_aes_128_gcm_init( gcm, key, iv );
    FD_TEST( !fd_aes_gcm_decrypt( gcm, ciphertext, actual_plaintext, sizeof(ciphertext), aad, sizeof(aad), actual_tag ) );
    BITFLIP( actual_tag, i );
  }

  /* Corrupt ciphertext */
  for( ulong i=0UL; i<(sizeof(actual_ciphertext)*8UL); i++ ) {
    BITFLIP( actual_ciphertext, i );
    fd_aes_128_gcm_init( gcm, key, iv );
    FD_TEST( !fd_aes_gcm_decrypt( gcm, actual_ciphertext, actual_plaintext, sizeof(ciphertext), aad, sizeof(aad), tag ) );
    BITFLIP( actual_ciphertext, i );
  }

  /* Corrupt associated data */
  uchar corrupt_aad[ 22 ]; fd_memcpy( corrupt_aad, aad, sizeof(aad) );
  for( ulong i=0UL; i<(sizeof(corrupt_aad)*8UL); i++ ) {
    BITFLIP( corrupt_aad, i );
    fd_aes_128_gcm_init( gcm, key, iv );
    FD_TEST( !fd_aes_gcm_decrypt( gcm, ciphertext, actual_plaintext, sizeof(ciphertext), corrupt_aad, sizeof(corrupt_aad), tag ) );
    BITFLIP( corrupt_aad, i );
  }

# undef BITFLIP

  FD_LOG_INFO(( "OK: AES-128-GCM auth (AES-NI)" ));
}

/* AES-GCM unroll tests ***********************************************/

FD_IMPORT_BINARY( fixture_aes_128_gcm_unroll, "src/ballet/aes/fixtures/gcm-ciphertext.bin" );

static void
test_aes_128_gcm_unroll( void ) {
  static uchar const key[ 16 ] =
    { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
      0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

  static uchar const iv[ 12 ] =
    { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
      0x08, 0x09, 0x0a, 0x0b };

  static uchar const plaintext[ 2048 ] = {0};

  static uchar const aad[ 32 ] =
    { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
      0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
      0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
      0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f };

  ulong const canary = 0xdb04fb2a65323a97; /* random */

  for( ulong j=1; j<sizeof(plaintext); j++ ) {

    uchar result[ 2056 ];
    uchar tag[ 16 ];
    FD_STORE( ulong, result+j, canary );

    fd_aes_gcm_t gcm[1];
    fd_aes_128_gcm_init( gcm, key, iv );
    fd_aes_gcm_encrypt( gcm, result, plaintext, j, aad, sizeof(aad), tag );

    if( FD_UNLIKELY( FD_LOAD( ulong, result+j )!=canary ) )
      FD_LOG_ERR(( "FAIL: buffer overrun detected" ));

    if( FD_UNLIKELY( 0!=memcmp( result, fixture_aes_128_gcm_unroll, j ) ) )
      FD_LOG_ERR(( "FAIL: AES-128-GCM unroll encrypt (AES-NI) for sz %lu (encrypt fail)", j ));

    fd_aes_128_gcm_init( gcm, key, iv );
    int ok = fd_aes_gcm_decrypt( gcm, fixture_aes_128_gcm_unroll, result, j, aad, sizeof(aad), tag );

    if( FD_UNLIKELY( FD_LOAD( ulong, result+j )!=canary ) )
      FD_LOG_ERR(( "FAIL: buffer overrun detected" ));

    if( FD_UNLIKELY( !ok || 0!=memcmp( result, plaintext, j ) ) )
      FD_LOG_ERR(( "FAIL: AES-128-GCM unroll decrypt (AES-NI) for sz %lu (decrypt fail)", j ));

  }
}

/* Main ***************************************************************/

int
main( int     argc,
      char ** argv ) {
  fd_boot( &argc, &argv );

  fd_rng_t _rng[1]; fd_rng_t * rng = fd_rng_join( fd_rng_new( _rng, 0U, 0UL ) );

# if FD_AES_IMPL == 0
  FD_LOG_NOTICE(( "Using AES-ECB portable backend" ));
# elif FD_AES_IMPL == 1
  FD_LOG_NOTICE(( "Using AES-ECB AESNI backend" ));
# endif

# if FD_AES_GCM_IMPL == 0
  FD_LOG_NOTICE(( "Using AES-GCM portable backend" ));
# elif FD_AES_GCM_IMPL == 1
  FD_LOG_NOTICE(( "Using AES-GCM AESNI backend" ));
# elif FD_AES_GCM_IMPL == 2
  FD_LOG_NOTICE(( "Using AES-GCM AVX2 AESNI backend" ));
# elif FD_AES_GCM_IMPL == 3
  FD_LOG_NOTICE(( "Using AES-GCM AVX512 VAES VPCLMUL backend" ));
# endif

  test_key_expansion_zeros( 128, fixture_key_expansion_128_zeros, 10 );

  test_aes_128_ecb();
  test_aes_128_gcm_bounds( rng );
  test_aes_128_gcm();
  test_aes_128_gcm_unroll();

  fd_rng_delete( fd_rng_leave( rng ) );
  FD_LOG_NOTICE(( "pass" ));
  fd_halt();
  return 0;
}
